home *** CD-ROM | disk | FTP | other *** search
/ Micromanía 92 / CDMM92_1.ISO / SOF 2 SDK / sof2sdk-101.msi / _92D6AC311BB48EBA344BBABC89DA6AB0 / _8A04BD287B4645DA864917705FF5C36B < prev    next >
Encoding:
Text File  |  2002-06-27  |  29.1 KB  |  1,203 lines

  1. // Copyright (C) 2001-2002 Raven Software
  2. //
  3. // cg_event.c -- handle entity events at snapshot or playerstate transitions
  4.  
  5. #include "cg_local.h"
  6. #include "..\ghoul2\g2.h"
  7. #include "../../ui/menudef.h"
  8.  
  9. //==========================================================================
  10.  
  11. /*
  12. ===================
  13. CG_PlaceString
  14.  
  15. Also called by scoreboard drawing
  16. ===================
  17. */
  18. const char    *CG_PlaceString( int rank ) {
  19.     static char    str[64];
  20.     char    *s, *t;
  21.  
  22.     if ( rank & RANK_TIED_FLAG ) {
  23.         rank &= ~RANK_TIED_FLAG;
  24.         t = "Tied for ";
  25.     } else {
  26.         t = "";
  27.     }
  28.  
  29.     if ( rank == 1 ) {
  30.         s = S_COLOR_BLUE "1st" S_COLOR_WHITE;        // draw in blue
  31.     } else if ( rank == 2 ) {
  32.         s = S_COLOR_RED "2nd" S_COLOR_WHITE;        // draw in red
  33.     } else if ( rank == 3 ) {
  34.         s = S_COLOR_YELLOW "3rd" S_COLOR_WHITE;        // draw in yellow
  35.     } else if ( rank == 11 ) {
  36.         s = "11th";
  37.     } else if ( rank == 12 ) {
  38.         s = "12th";
  39.     } else if ( rank == 13 ) {
  40.         s = "13th";
  41.     } else if ( rank % 10 == 1 ) {
  42.         s = va("%ist", rank);
  43.     } else if ( rank % 10 == 2 ) {
  44.         s = va("%ind", rank);
  45.     } else if ( rank % 10 == 3 ) {
  46.         s = va("%ird", rank);
  47.     } else {
  48.         s = va("%ith", rank);
  49.     }
  50.  
  51.     Com_sprintf( str, sizeof( str ), "%s%s", t, s );
  52.     return str;
  53. }
  54.  
  55. /*
  56. =============
  57. CG_GameOver
  58. =============
  59. */
  60. static void CG_GameOver ( entityState_t *ent )
  61. {
  62.     switch ( ent->eventParm )
  63.     {
  64.         case GAME_OVER_TIMELIMIT:
  65.             Com_sprintf ( cgs.gameover, MAX_QPATH, "Timelimit Hit" );
  66.             break;
  67.  
  68.         case GAME_OVER_SCORELIMIT:
  69.             if ( cgs.gametypeData->teams )
  70.             {
  71.                 switch ( ent->otherEntityNum )
  72.                 {
  73.                     case TEAM_RED:                    
  74.                         Com_sprintf ( cgs.gameover, MAX_QPATH, "Red Team hit the score limit" );
  75.                         break;
  76.  
  77.                     case TEAM_BLUE:
  78.                         Com_sprintf ( cgs.gameover, MAX_QPATH, "Blue Team hit the score limit" );
  79.                         break;
  80.                 }
  81.             }
  82.             else
  83.             {
  84.                 Com_sprintf ( cgs.gameover, MAX_QPATH, "%s" S_COLOR_WHITE " hit the score limit", cgs.clientinfo[ent->otherEntityNum].name );
  85.             }
  86.             break;
  87.  
  88.         default:
  89.             return;
  90.     }     
  91.  
  92.     CG_CenterPrint ( cgs.gameover, 0.43f );
  93.  
  94.     Com_Printf ( "@%s\n", cgs.gameover );
  95. }
  96.  
  97. /*
  98. =============
  99. CG_Obituary
  100. =============
  101. */
  102. static void CG_Obituary( entityState_t *ent ) 
  103. {
  104.     int                mod;
  105.     int                target, attacker;
  106.     char            *message;
  107.     char            *message2;
  108.     const char        *targetInfo;
  109.     const char        *attackerInfo;
  110.     char            targetName[32];
  111.     char            attackerName[32];
  112.     const char        *targetColor;
  113.     const char        *attackerColor;
  114.     attackType_t    attack;
  115.     gender_t        gender;
  116.     clientInfo_t    *ci;
  117.  
  118.     target        = ent->otherEntityNum;
  119.     attacker    = ent->otherEntityNum2;
  120.     mod            = ent->eventParm & 0xFF;
  121.     attack        = (ent->eventParm >> 8) & 0xFF;
  122.     attackerColor = S_COLOR_WHITE;
  123.     targetColor   = S_COLOR_WHITE;
  124.  
  125.     if ( target < 0 || target >= MAX_CLIENTS ) 
  126.     {
  127.         Com_Error( ERR_FATAL, "CG_Obituary: target out of range" );
  128.     }
  129.  
  130.     // Play the death sound, water if they drowned
  131.     if ( mod == MOD_WATER )
  132.     {
  133.         trap_S_StartSound ( NULL, target, CHAN_AUTO, cgs.media.drownDeathSound, -1, -1 );
  134.     }
  135.     else
  136.     {
  137.         trap_S_StartSound( NULL, target, CHAN_VOICE, CG_CustomPlayerSound(target, SOUND_DIE_1 + (cg.time%3)), -1, -1);
  138.     }
  139.  
  140.     // Play the frag sound, and make sure its not played more than every 250ms
  141.     if ( cg.time - cg.lastKillTime > 250 && attacker == cg.snap->ps.clientNum )
  142.     {
  143.         if ( cg_soundFrag.integer )
  144.         {
  145.             // If the attacker killed themselves play the selffrag sound
  146.             if ( attacker == target )
  147.             {
  148.                 trap_S_StartLocalSound ( cgs.media.fragSelfSound, CHAN_AUTO );
  149.             }
  150.             else
  151.             {
  152.                 // In a team game a kill of a teammate will play the self frag sound rather 
  153.                 // than the frag sound
  154.                 if ( cgs.gametypeData->teams )
  155.                 {
  156.                     if ( cgs.clientinfo[target].team == cgs.clientinfo[attacker].team )
  157.                     {
  158.                         trap_S_StartLocalSound ( cgs.media.fragSelfSound, CHAN_AUTO );
  159.                     }
  160.                     else
  161.                     {
  162.                         trap_S_StartLocalSound ( cgs.media.fragSound, CHAN_AUTO );
  163.                     }
  164.                 }
  165.                 else
  166.                 {
  167.                     trap_S_StartLocalSound ( cgs.media.fragSound, CHAN_AUTO );
  168.                 }
  169.             }
  170.         }
  171.  
  172.         cg.lastKillTime = cg.time;
  173.     }            
  174.  
  175.     ci = &cgs.clientinfo[target];
  176.  
  177.     if ( attacker < 0 || attacker >= MAX_CLIENTS ) 
  178.     {
  179.         attacker = ENTITYNUM_WORLD;
  180.         attackerInfo = NULL;
  181.     } 
  182.     else 
  183.     {
  184.         attackerInfo = CG_ConfigString( CS_PLAYERS + attacker );
  185.     }
  186.  
  187.     targetInfo = CG_ConfigString( CS_PLAYERS + target );
  188.     if ( !targetInfo ) {
  189.         return;
  190.     }
  191.     Q_strncpyz( targetName, Info_ValueForKey( targetInfo, "n" ), sizeof(targetName) - 2);
  192.     strcat( targetName, S_COLOR_WHITE );
  193.  
  194.     switch ( cgs.clientinfo[target].team )
  195.     {
  196.         case TEAM_RED:
  197.             targetColor = S_COLOR_RED;
  198.             break;
  199.  
  200.         case TEAM_BLUE:
  201.             targetColor = S_COLOR_BLUE;
  202.             break;
  203.     }
  204.  
  205.     message2 = "";
  206.  
  207.     // check for single client messages
  208.  
  209.     gender = ci->gender;
  210.  
  211.     switch( mod ) 
  212.     {
  213.         case MOD_SUICIDE:
  214.             message = "suicides";
  215.             break;
  216.         case MOD_FALLING:
  217.             if ( gender == GENDER_FEMALE )
  218.                 message = "fell to her death";
  219.             else
  220.                 message = "fell to his death";
  221.             break;
  222.         case MOD_CRUSH:
  223.             message = "was squished";
  224.             break;
  225.         case MOD_WATER:
  226.             message = "sank like a rock";
  227.             break;
  228.         case MOD_TARGET_LASER:
  229.             message = "saw the light";
  230.             break;
  231.         case MOD_TRIGGER_HURT:
  232.         case MOD_TRIGGER_HURT_NOSUICIDE:
  233.             message = "was in the wrong place";
  234.             break;
  235.         case MOD_TEAMCHANGE:
  236.             return;
  237.  
  238.         default:
  239.             message = NULL;
  240.             break;
  241.     }
  242.  
  243.     // Attacker killed themselves.  Ridicule them for it.
  244.     if (attacker == target) 
  245.     {
  246.         switch (mod) 
  247.         {
  248.             case MOD_MM1_GRENADE_LAUNCHER:    
  249.             case MOD_RPG7_LAUNCHER:           
  250.             case MOD_M84_GRENADE:
  251.             case MOD_SMOHG92_GRENADE:
  252.             case MOD_ANM14_GRENADE:
  253.             case MOD_M15_GRENADE:
  254.                 if ( gender == GENDER_FEMALE )
  255.                     message = "blew herself up";
  256.                 else if ( gender == GENDER_NEUTER )
  257.                     message = "blew itself up";
  258.                 else
  259.                     message = "blew himself up";
  260.                 break;
  261.  
  262.             default:
  263.                 if ( gender == GENDER_FEMALE )
  264.                     message = "killed herself";
  265.                 else if ( gender == GENDER_NEUTER )
  266.                     message = "killed itself";
  267.                 else
  268.                     message = "killed himself";
  269.                 break;
  270.         }
  271.     }
  272.  
  273.     if (message) 
  274.     {
  275.         Com_Printf( "%s%s %s.\n", targetColor, targetName, message);
  276.         return;
  277.     }
  278.  
  279.     // check for kill messages from the current clientNum when
  280.     // not in a team game.
  281.     if ( cgs.gametypeData->showKills )
  282.     {
  283.         if ( attacker == cg.snap->ps.clientNum ) 
  284.         {
  285.             char    *s;
  286.  
  287.             if ( !cgs.gametypeData->teams ) 
  288.             {
  289.                 s = va("You killed %s%s\n%s place with %i", targetColor, targetName, 
  290.                     CG_PlaceString( cg.snap->ps.persistant[PERS_RANK] + 1 ),
  291.                     cg.snap->ps.persistant[PERS_SCORE] );
  292.             } 
  293.             else 
  294.             {
  295.                 s = va("You killed %s%s", targetColor, targetName );
  296.             }
  297.  
  298.             CG_CenterPrint( s, 0.43f );
  299.         }
  300.     }
  301.  
  302.  
  303.     // check for double client messages
  304.     if ( !attackerInfo ) 
  305.     {
  306.         attacker = ENTITYNUM_WORLD;
  307.         strcpy( attackerName, "noname" );
  308.     } 
  309.     else 
  310.     {
  311.         Q_strncpyz( attackerName, Info_ValueForKey( attackerInfo, "n" ), sizeof(attackerName) - 2);
  312.         strcat( attackerName, S_COLOR_WHITE );
  313.         // check for kill messages about the current clientNum
  314.         if ( target == cg.snap->ps.clientNum ) 
  315.         {
  316.             Q_strncpyz( cg.killerName, attackerName, sizeof( cg.killerName ) );
  317.         }
  318.  
  319.         switch ( cgs.clientinfo[attacker].team )
  320.         {
  321.             case TEAM_RED:
  322.                 attackerColor = S_COLOR_RED;
  323.                 break;
  324.  
  325.             case TEAM_BLUE:
  326.                 attackerColor = S_COLOR_BLUE;
  327.                 break;
  328.         }
  329.     }
  330.  
  331.             
  332.     if ( attacker != ENTITYNUM_WORLD ) 
  333.     {
  334.         switch (mod) 
  335.         {
  336.             case MOD_KNIFE:
  337.                 message = "was sliced by";
  338.                 break;
  339.  
  340.             case MOD_USAS_12_SHOTGUN:
  341.             case MOD_M590_SHOTGUN:
  342.                 if ( attack == ATTACK_ALTERNATE )
  343.                 {
  344.                     message = "was bludgeoned by";
  345.                     message2 = va("'s %s", weaponParseInfo[mod].mName );
  346.                 }
  347.                 else
  348.                 {
  349.                     message = "was pumped full of lead by";
  350.                     message2 = va("'s %s", weaponParseInfo[mod].mName );
  351.                 }
  352.                 break;
  353.  
  354.             case MOD_M1911A1_PISTOL:
  355.             case MOD_USSOCOM_PISTOL: 
  356.                 if ( attack == ATTACK_ALTERNATE )
  357.                 {
  358.                     message = "was pistol whipped by";
  359.                     message2 = va("'s %s", weaponParseInfo[mod].mName );
  360.                 }
  361.                 else
  362.                 {
  363.                     message = "was shot by";
  364.                     message2 = va("'s %s", weaponParseInfo[mod].mName );
  365.                 }
  366.                 break;
  367.  
  368.             case MOD_AK74_ASSAULT_RIFLE:
  369.                 if ( attack == ATTACK_ALTERNATE )
  370.                 {
  371.                     message = "was stabbed by";
  372.                 }
  373.                 else
  374.                 {
  375.                     message = "was shot by";
  376.                     message2 = va("'s %s", weaponParseInfo[mod].mName );
  377.                 }
  378.                 break;
  379.  
  380.             case MOD_M60_MACHINEGUN:
  381.             case MOD_MICRO_UZI_SUBMACHINEGUN:
  382.             case MOD_MP5:
  383.             case MOD_M3A1_SUBMACHINEGUN:
  384.             case MOD_M4_ASSAULT_RIFLE:
  385.                 message = "was shot by";
  386.                 message2 = va("'s %s", weaponParseInfo[mod].mName );
  387.                 break;
  388.  
  389.             case MOD_MSG90A1_SNIPER_RIFLE:    
  390.                 message = "was sniped by";
  391.                 message2 = va("'s %s", weaponParseInfo[mod].mName );
  392.                 break;
  393.  
  394.             case MOD_MM1_GRENADE_LAUNCHER:    
  395.             case MOD_RPG7_LAUNCHER:           
  396.             case MOD_M84_GRENADE:
  397.             case MOD_SMOHG92_GRENADE:
  398.             case MOD_ANM14_GRENADE:
  399.             case MOD_M15_GRENADE:
  400.                 message = "was detonated by";
  401.                 message2 = va("'s %s", weaponParseInfo[mod].mName );
  402.                 break;
  403.  
  404.             case MOD_TELEFRAG:
  405.                 message = "tried to invade";
  406.                 message2 = "'s personal space";
  407.                 break;
  408.  
  409.             default:
  410.                 message = "was killed by";
  411.                 break;
  412.         }
  413.  
  414.         if (message) {
  415.             Com_Printf( "%s%s %s %s%s%s\n", targetColor, targetName, message, attackerColor, attackerName, message2);
  416.             return;
  417.         }
  418.     }
  419.  
  420.     // we don't know what it was
  421.     Com_Printf( "%s%s died.\n", targetColor, targetName );
  422. }
  423.  
  424. /*
  425. ================
  426. CG_ItemPickup
  427.  
  428. A new item was picked up this frame
  429. ================
  430. */
  431. static void CG_ItemPickup( int itemNum, qboolean autoswitch ) 
  432. {
  433.     cg.itemPickup = itemNum;
  434.  
  435.     // see if it should be the grabbed weapon
  436.     if ( cg_autoswitch.integer && bg_itemlist[itemNum].giType == IT_WEAPON && autoswitch ) 
  437.     {
  438.         if ( cg_autoswitch.integer >= 2 )
  439.         {
  440.             if ( weaponData[bg_itemlist[itemNum].giTag].safe )
  441.             {
  442.                 cg.weaponSelectTime = cg.time;
  443.                 cg.weaponSelect = bg_itemlist[itemNum].giTag;
  444.             }
  445.         }
  446.         else
  447.         {
  448.             cg.weaponSelectTime = cg.time;
  449.             cg.weaponSelect = bg_itemlist[itemNum].giTag;
  450.         }
  451.     }
  452.  
  453.     Com_Printf ( "You picked up %s %s!\n", bg_itemlist[itemNum].pickup_prefix, bg_itemlist[itemNum].pickup_name );
  454. }
  455.  
  456.  
  457. /*
  458. ================
  459. CG_PainEvent
  460.  
  461. Also called by playerstate transition
  462. ================
  463. */
  464. void CG_PainEvent( centity_t *cent, int health ) 
  465. {
  466.     ECustomSounds    sound;
  467.  
  468.     // don't do more than two pain sounds a second
  469.     if ( cg.time - cent->pe.painTime < 500 ) {
  470.         return;
  471.     }
  472.  
  473.     if (health <= 0 )
  474.     {
  475.         return;
  476.     }
  477.     else if ( health < 25 ) 
  478.     {
  479.         sound = SOUND_PAIN_1;
  480.     } 
  481.     else if ( health < 50 ) 
  482.     {
  483.         sound = SOUND_PAIN_2;
  484.     } 
  485.     else if ( health < 75 ) 
  486.     {
  487.         sound = SOUND_PAIN_3;
  488.     } 
  489.     else 
  490.     {
  491.         sound = SOUND_PAIN_3;
  492.     }
  493.     trap_S_StartSound( NULL, cent->currentState.number, CHAN_VOICE, 
  494.         CG_CustomPlayerSound( cent->currentState.number, sound ), -1, -1 );
  495.  
  496.     // save pain time for programitic twitch animation
  497.     cent->pe.painTime = cg.time;
  498.     cent->pe.painDirection    ^= 1;
  499. }
  500.  
  501.  
  502. static void CG_BodyQueueCopy(centity_t *cent, int clientNum, int hitLocation, vec3_t direction )
  503. {
  504.     centity_t        *source;
  505.     animation_t        *anim;
  506.     float            animSpeed;
  507.     int                flags=BONE_ANIM_OVERRIDE_FREEZE;
  508.     clientInfo_t    *ci;
  509.     int                i;
  510.  
  511.     if (cent->ghoul2)
  512.     {
  513.         trap_G2API_CleanGhoul2Models(¢->ghoul2);
  514.         cent->ghoul2 = 0;
  515.     }
  516.  
  517.     if (clientNum < 0 || clientNum >= MAX_CLIENTS)
  518.     {
  519.         return;
  520.     }
  521.  
  522.     source = CG_GetEntity ( clientNum );
  523.  
  524.     ci = &cgs.clientinfo[ clientNum ];
  525.  
  526.     cent->radius = 100;
  527.  
  528.     if (!source)
  529.     {
  530.         return;
  531.     }
  532.  
  533.     // Make sure the player model is updated before copying it to the body queue
  534.     CG_UpdatePlayerModel ( source );
  535.  
  536.     if (!source->ghoul2)
  537.     {    
  538.         // some how we don't have a g2 model, so don't do anything
  539.         return;
  540.     }
  541.  
  542.     // Remove the weapon bolt for the death
  543.     if ( source->pe.weaponModelSpot )
  544.     {
  545.         trap_G2API_DetachG2Model ( source->ghoul2, source->pe.weaponModelSpot );
  546.         trap_G2API_RemoveGhoul2Model ( &source->ghoul2, source->pe.weaponModelSpot );
  547.         source->pe.weaponModelSpot = 0;
  548.  
  549.         source->flashBoltInterface.isValid = qfalse;
  550.         source->ejectBoltInterface.isValid = qfalse;
  551.     }
  552.  
  553.     trap_G2API_DuplicateGhoul2Instance(source->ghoul2, ¢->ghoul2);
  554.     
  555.     if ( !cent->ghoul2 )
  556.     {
  557.         return;
  558.     }
  559.  
  560.     // Reset all mision bolt positions
  561.     for ( i = 0; i < MAX_GAMETYPE_ITEMS; i ++ )
  562.     {
  563.         ci->boltGametypeItems[i] = -1;
  564.     }
  565.  
  566.     ci->boltNightvision = -1;
  567.  
  568.     // Clear the source's ghoul2 model to force it to be re-duplicatd.  This will
  569.     // then cause all the gore attached to it to be cleared
  570.     trap_G2API_CleanGhoul2Models ( &source->ghoul2 );
  571.     source->ghoul2 = NULL;
  572.  
  573.     if (cg_lockDeaths.integer)
  574.     {
  575.         anim = &ci->animations[ BOTH_DEATH_CHEST_1 ];
  576.     }
  577.     else
  578.     {
  579.         anim = &ci->animations[ cent->currentState.torsoAnim & ~(ANIM_TOGGLEBIT) ];
  580.     }
  581.     animSpeed = 50.0f / anim->frameLerp;
  582.  
  583.     // Clear any bone angles
  584.  
  585.     trap_G2API_SetBoneAngles(cent->ghoul2, 0, "lower_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 0, cg.time ); 
  586.     trap_G2API_SetBoneAngles(cent->ghoul2, 0, "upper_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 0, cg.time ); 
  587.     trap_G2API_SetBoneAngles(cent->ghoul2, 0, "cranium", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, POSITIVE_X, cgs.gameModels,0, cg.time ); 
  588.  
  589.     // Set the death animation
  590.     trap_G2API_SetBoneAnim(cent->ghoul2, 0, "model_root", anim->firstFrame, anim->firstFrame + anim->numFrames, flags, animSpeed, cg.time, -1, 150);
  591.     trap_G2API_SetBoneAnim(cent->ghoul2, 0, "lower_lumbar", anim->firstFrame, anim->firstFrame + anim->numFrames, flags, animSpeed, cg.time, -1, 150);
  592.  
  593.     // hit location is a bit field and we need to iterate through the bits
  594.     for ( i = 0; (1<<i) < HL_MAX; i++ )
  595.     {
  596.         if (hitLocation & (1<<i))
  597.         {
  598.             CG_ApplyGore(clientNum, cent, i+1, direction);
  599.         }
  600.     }
  601. }
  602.  
  603. /*
  604. ==============
  605. CG_EntityEvent
  606.  
  607. An entity has an event value
  608. also called by CG_CheckPlayerstateEvents
  609. ==============
  610. */
  611. #define    DEBUGNAME(x) if(cg_debugEvents.integer){Com_Printf(x"\n");}
  612. void CG_EntityEvent( centity_t *cent, vec3_t position ) 
  613. {
  614.     entityState_t    *es;
  615.     int                event;
  616.     vec3_t            dir;
  617.     const char        *s;
  618.     int                clientNum;
  619.     clientInfo_t    *ci;
  620.  
  621.     es = ¢->currentState;
  622.     event = es->event & ~EV_EVENT_BITS;
  623.  
  624.     if ( cg_debugEvents.integer ) 
  625.     {
  626.         Com_Printf( "ent:%3i  event:%3i ", es->number, event );
  627.     }
  628.  
  629.     // Ignore all events until map is done changing
  630.     if ( cg.mMapChange )
  631.     {
  632.         return;
  633.     }
  634.  
  635.     if ( !event ) 
  636.     {
  637.         DEBUGNAME("ZEROEVENT");
  638.         return;
  639.     }
  640.  
  641.     clientNum = es->clientNum;
  642.     if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) 
  643.     {
  644.         clientNum = 0;
  645.     }
  646.  
  647.     ci = &cgs.clientinfo[ clientNum ];
  648.  
  649.     switch ( event ) 
  650.     {
  651.         //
  652.         // movement generated events
  653.         //
  654.         case EV_FOOTSTEP:
  655.             DEBUGNAME("EV_FOOTSTEP");
  656.             if (cg_footsteps.integer) 
  657.             {
  658.                 trap_S_StartSound (NULL, es->number, CHAN_BODY, trap_MAT_GetSound(MAT_FOOTSTEP_NORMAL, (es->eventParm&MATERIAL_MASK)), 180, 1000 );
  659.             }
  660.             break;
  661.  
  662.         case EV_FOOTWADE:
  663.             DEBUGNAME("EV_FOOTWADE");
  664.             break;
  665.  
  666.         case EV_FALL_SHORT:
  667.             DEBUGNAME("EV_FALL_SHORT");
  668.             trap_S_StartSound (NULL, es->number, CHAN_AUTO, trap_MAT_GetSound(MAT_LAND_NORMAL, es->eventParm&MATERIAL_MASK), 150, 900 );
  669.             if ( clientNum == cg.predictedPlayerState.clientNum ) 
  670.             {
  671.                 // smooth landing z changes
  672.                 cg.landChange = -8;
  673.                 cg.landTime = cg.time;
  674.             }
  675.             break;
  676.     
  677.         case EV_FALL_MEDIUM:
  678.             DEBUGNAME("EV_FALL_MEDIUM");
  679.             // use normal pain sound
  680.             trap_S_StartSound (NULL, es->number, CHAN_AUTO, trap_MAT_GetSound(MAT_LAND_PAIN, (es->eventParm>>8)&MATERIAL_MASK), 150, 900 );
  681.             trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomPlayerSound( es->number, SOUND_PAIN_3 ), -1, -1 );
  682.             if ( clientNum == cg.predictedPlayerState.clientNum ) 
  683.             {
  684.                 // smooth landing z changes
  685.                 cg.landChange = -16;
  686.                 cg.landTime = cg.time;
  687.             }
  688.             break;
  689.     
  690.         case EV_FALL_FAR:
  691.             DEBUGNAME("EV_FALL_FAR");
  692.     
  693.             trap_S_StartSound (NULL, es->number, CHAN_AUTO, trap_MAT_GetSound(MAT_LAND_DEATH, (es->eventParm>>8)&MATERIAL_MASK), -1, -1 );
  694.             trap_S_StartSound (NULL, es->number, CHAN_AUTO, CG_CustomPlayerSound( cent->currentState.number, SOUND_PAIN_2 ), 150, 900 );
  695.         
  696.             // don't play a pain sound right after this
  697.             cent->pe.painTime = cg.time;    
  698.             
  699.             if ( clientNum == cg.predictedPlayerState.clientNum ) 
  700.             {
  701.                 // smooth landing z changes
  702.                 cg.landChange = -24;
  703.                 cg.landTime = cg.time;
  704.             }
  705.             break;
  706.  
  707.         case EV_STEP_4:
  708.         case EV_STEP_8:
  709.         case EV_STEP_12:
  710.         case EV_STEP_16:        // smooth out step up transitions
  711.         {
  712.             float    oldStep;
  713.             int        delta;
  714.             int        step;
  715.  
  716.             DEBUGNAME("EV_STEP");
  717.  
  718.             if ( clientNum != cg.predictedPlayerState.clientNum ) {
  719.                 break;
  720.             }
  721.             // if we are interpolating, we don't need to smooth steps
  722.             if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW) ||
  723.                 cg_nopredict.integer || cg_synchronousClients.integer ) {
  724.                 break;
  725.             }
  726.             // check for stepping up before a previous step is completed
  727.             delta = cg.time - cg.stepTime;
  728.             
  729.             if (delta < STEP_TIME) 
  730.             {
  731.                 oldStep = cg.stepChange * (STEP_TIME - delta) / STEP_TIME;
  732.             } 
  733.             else 
  734.             {
  735.                 oldStep = 0;
  736.             }
  737.  
  738.             // add this amount
  739.             step = 4 * (event - EV_STEP_4 + 1 );
  740.             cg.stepChange = oldStep + step;
  741.             
  742.             if ( cg.stepChange > MAX_STEP_CHANGE ) 
  743.             {
  744.                 cg.stepChange = MAX_STEP_CHANGE;
  745.             }
  746.             cg.stepTime = cg.time;
  747.  
  748.             break;
  749.         }
  750.  
  751.         case EV_JUMP:
  752.             DEBUGNAME("EV_JUMP");
  753.             trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*jump1.wav" ), -1, -1 );
  754.             break;
  755.  
  756.         case EV_WATER_FOOTSTEP:
  757.             DEBUGNAME("EV_WATER_FOOTSTEP");
  758.             trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.waterFootstep[rand()%2], -1, -1);
  759.             break;
  760.  
  761.         case EV_WATER_TOUCH:
  762.             DEBUGNAME("EV_WATER_TOUCH");
  763.             trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.waterFootstep[rand()%2], -1, -1);
  764.             break;
  765.  
  766.         case EV_WATER_LAND:
  767.             DEBUGNAME("EV_WATER_LAND");
  768.             trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.waterJumpIn, -1, -1 );
  769.             break;
  770.  
  771.         case EV_WATER_CLEAR:
  772.             DEBUGNAME("EV_WATER_CLEAR");
  773.             trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.waterLeave, -1, -1 );
  774.             break;
  775.  
  776.         case EV_SWIM:
  777.             DEBUGNAME("EV_SWIM");
  778.             trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.waterWade[rand()%2], -1, -1 );
  779.             break;
  780.  
  781.         case EV_ITEM_PICKUP:
  782.         {
  783.             gitem_t        *item;
  784.             int            index;
  785.             qboolean    autoswitch = qfalse;
  786.  
  787.             DEBUGNAME("EV_ITEM_PICKUP");
  788.  
  789.             // Dtermine if this item should autoswitch
  790.             autoswitch = (es->eventParm & ITEM_AUTOSWITCHBIT)?qtrue:qfalse;
  791.  
  792.             // player predicted index
  793.             index = es->eventParm & ~ITEM_AUTOSWITCHBIT;        
  794.  
  795.             if ( index < 1 || index >= bg_numItems ) 
  796.             {
  797.                 break;
  798.             }
  799.  
  800.             item = &bg_itemlist[ index ];
  801.  
  802.             if ( item->pickup_sound )
  803.             {
  804.                 trap_S_StartSound (NULL, es->number, CHAN_AUTO,    trap_S_RegisterSound( item->pickup_sound ), -1, -1 );
  805.             }
  806.  
  807.             // show icon and name on status bar
  808.             if ( es->number == cg.snap->ps.clientNum ) 
  809.             {
  810.                 if ( cg.predictedPlayerState.pm_type == PM_NORMAL )
  811.                 {
  812.                     CG_ItemPickup( index, autoswitch );
  813.                 }
  814.             }
  815.  
  816.             break;
  817.         }
  818.  
  819.         //=================================================================
  820.         //
  821.         // weapon events
  822.         //
  823.         //=================================================================
  824.         case EV_CHANGE_WEAPON_CANCELLED:
  825.         case EV_CHANGE_WEAPON:
  826.  
  827.             DEBUGNAME("EV_CHANGE_WEAPON");
  828.             
  829.             // Determine whether or not the alt fire popup should show up
  830.             if(es->number==cg.snap->ps.clientNum && cg.weaponMenuUp )
  831.             {
  832.                 // done with weapon menu
  833.                 cg.weaponMenuUp = qfalse;
  834.  
  835.                 cg.weaponSelect = cg.weaponMenuSelect;
  836.             }
  837.  
  838.             break;
  839.  
  840.         case EV_READY_WEAPON:
  841.             DEBUGNAME("EV_READY_WEAPON");
  842.             break;    
  843.  
  844.         case EV_FIRE_WEAPON:
  845.             DEBUGNAME("EV_FIRE_WEAPON");
  846.             CG_FireWeapon( cent, ATTACK_NORMAL );
  847.             break;
  848.  
  849.         case EV_ALT_FIRE:
  850.             DEBUGNAME("EV_ALT_FIRE");
  851.             CG_FireWeapon( cent, ATTACK_ALTERNATE );
  852.             break;
  853.  
  854.         case EV_NOAMMO:
  855.             DEBUGNAME("EV_NOAMMO");
  856.  
  857.             if(es->number==cg.snap->ps.clientNum)
  858.             {
  859.                 CG_OutOfAmmoChange( es->eventParm );
  860.             }
  861.             break;
  862.  
  863.         case EV_ITEM_POP:
  864.             DEBUGNAME("EV_ITEM_POP");
  865.             break;
  866.  
  867.         case EV_ITEM_RESPAWN:
  868.             DEBUGNAME("EV_ITEM_RESPAWN");
  869.             trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.itemRespawnSound, -1, -1 );
  870.             cent->miscTime = cg.time;
  871.             break;
  872.  
  873.         //=================================================================
  874.         //
  875.         // other events
  876.         //
  877.         //=================================================================
  878.  
  879.         case EV_PLAYER_TELEPORT_IN:
  880.             DEBUGNAME("EV_PLAYER_TELEPORT_IN");
  881.             if ( cgs.gametypeData->respawnType != RT_NONE )
  882.             {
  883.                 trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.respawnSound, -1, -1 );
  884.             }
  885.             break;
  886.  
  887.         case EV_PLAYER_TELEPORT_OUT:
  888.             DEBUGNAME("EV_PLAYER_TELEPORT_OUT");
  889.             break;
  890.  
  891.         case EV_GRENADE_BOUNCE:
  892.             DEBUGNAME("EV_GRENADE_BOUNCE");
  893.             if ( rand() & 1 ) 
  894.             {
  895.                 trap_S_StartSound (NULL, es->number, CHAN_AUTO, trap_MAT_GetSound(MAT_BOUNCEMETAL_1, (es->eventParm&MATERIAL_MASK)), -1, -1 );
  896.             } 
  897.             else 
  898.             {
  899.                 trap_S_StartSound (NULL, es->number, CHAN_AUTO, trap_MAT_GetSound(MAT_BOUNCEMETAL_2, (es->eventParm&MATERIAL_MASK)), -1, -1 );
  900.             }
  901.             break;
  902.  
  903.         case EV_DESTROY_GHOUL2_INSTANCE:
  904.         {
  905.             centity_t* cent2 = CG_GetEntity (es->eventParm);
  906.             DEBUGNAME("EV_DESTROY_GHOUL2_INSTANCE");
  907.             if ( cent2->ghoul2 && trap_G2_HaveWeGhoul2Models( cent2->ghoul2))
  908.             {
  909.                 trap_G2API_CleanGhoul2Models(&(cent2->ghoul2));
  910.             }
  911.             break;
  912.         }
  913.         
  914.     //=================================================================
  915.  
  916.     //
  917.     // missile impacts
  918.     //
  919.  
  920.     case EV_MISSILE_HIT:
  921.         DEBUGNAME("EV_MISSILE_HIT");
  922.         ByteToDir( (es->eventParm >> MATERIAL_BITS), dir );
  923.         CG_MissileHitPlayer( es->weapon, position, dir, es->otherEntityNum,
  924.                             (cent->currentState.eFlags & EF_ALT_FIRING)?ATTACK_ALTERNATE:ATTACK_NORMAL );
  925.         if ( es->otherEntityNum != cg.snap->ps.clientNum ) 
  926.         {
  927.             // Some missiles - e.g. thrown knives stick in players (for visual effect only).
  928.             CG_HandleStickyMissile(cent,es,dir,es->otherEntityNum);
  929.         }
  930.         break;
  931.  
  932.     case EV_MISSILE_MISS:
  933.         DEBUGNAME("EV_MISSILE_MISS");
  934.         ByteToDir( (es->eventParm >> MATERIAL_BITS), dir );
  935.         CG_MissileHitWall(es->weapon, position, dir, 
  936.                         (es->eventParm & MATERIAL_MASK), (cent->currentState.eFlags & EF_ALT_FIRING)?ATTACK_ALTERNATE:ATTACK_NORMAL );
  937.         break;
  938.  
  939.     case EV_BULLET_HIT_WALL:
  940.         DEBUGNAME("EV_BULLET_HIT_WALL");
  941.         
  942.         if ( !(cg_antiLag.integer && cg_impactPrediction.integer && es->otherEntityNum == cg.predictedPlayerState.clientNum ) )
  943.         {
  944.             // eventParm contains the direction byte and the material id
  945.             ByteToDir( (es->eventParm >> MATERIAL_BITS), dir );
  946.             
  947.             // time contains the weapon and attack of the shot
  948.             CG_Bullet( es->pos.trBase, es->otherEntityNum, (es->time&0xFF), 
  949.                        dir, ENTITYNUM_WORLD, (es->eventParm & MATERIAL_MASK),
  950.                        ((es->time>>8)&0xFF) );
  951.         }
  952.  
  953.         break;
  954.  
  955.     case EV_BULLET_HIT_FLESH:
  956.  
  957.         DEBUGNAME("EV_BULLET_HIT_FLESH");
  958.  
  959.         // Play hit sounds for local player
  960.         if ( es->otherEntityNum2 == cg.snap->ps.clientNum )
  961.         {
  962.             if ( cg.snap->ps.stats[STAT_ARMOR] )
  963.             {
  964.                 trap_S_StartLocalSound ( cgs.media.armorHitSound[rand()%2], CHAN_AUTO  );
  965.             }
  966.             else
  967.             {
  968.                 trap_S_StartLocalSound ( cgs.media.fleshHitSound[rand()%2], CHAN_AUTO  );
  969.             }
  970.         }
  971.  
  972. #ifdef _SOF2_FLESHIMPACTPREDICTION
  973.         if ( !(cg_antiLag.integer && cg_impactPrediction.integer >= 2 && es->otherEntityNum == cg.predictedPlayerState.clientNum ) )
  974. #endif
  975.         {
  976.             int    fxtype = MATERIAL_FLESH;
  977.  
  978.             // eventParm contains the direction byte
  979.             ByteToDir( es->eventParm, dir );
  980.  
  981.             if (cg_lockBlood.integer)
  982.             {
  983.                 fxtype = MATERIAL_NONE;
  984.             }
  985.  
  986.             CG_Bullet( es->pos.trBase, es->otherEntityNum, (es->time&0xFF), dir,  
  987.                        es->otherEntityNum2, fxtype,
  988.                        ((es->time>>8)&0xFF) );
  989.  
  990.             CG_AddProcGore ( cent );
  991.         }
  992.  
  993.         break;
  994.  
  995.     case EV_EXPLOSION_HIT_FLESH:
  996.  
  997.         DEBUGNAME("EV_EXPLOSION_HIT_FLESH");
  998.  
  999.         CG_AddProcGore ( cent );
  1000.         break;
  1001.  
  1002.     case EV_PLAY_EFFECT:
  1003.         DEBUGNAME("EV_PLAY_EFFECT");
  1004.         if (es->eventParm != -1)
  1005.         {
  1006.             trap_FX_PlayEffectID(es->eventParm, es->origin, es->angles, -1, -1 );
  1007.         }
  1008.         break;
  1009.  
  1010.     case EV_GENERAL_SOUND:
  1011.         DEBUGNAME("EV_GENERAL_SOUND");
  1012.         if ( cgs.gameSounds[ es->eventParm ] ) {
  1013.             trap_S_StartSound (NULL, es->number, CHAN_VOICE, cgs.gameSounds[ es->eventParm ], -1, -1 );
  1014.         } else {
  1015.             s = CG_ConfigString( CS_SOUNDS + es->eventParm );
  1016.             trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, s ), -1, -1 );
  1017.         }
  1018.         break;
  1019.  
  1020.     case EV_GLOBAL_SOUND:
  1021.         if ( cg_soundGlobal.integer )
  1022.         {
  1023.             DEBUGNAME("EV_GLOBAL_SOUND");
  1024.             if ( cgs.gameSounds[ es->eventParm ] ) 
  1025.             {
  1026.                 trap_S_StartLocalSound ( cgs.gameSounds[ es->eventParm ], CHAN_AUTO );
  1027.             } 
  1028.             else 
  1029.             {
  1030.                 s = CG_ConfigString( CS_SOUNDS + es->eventParm );
  1031.                 trap_S_StartLocalSound ( CG_CustomSound( es->number, s ), CHAN_AUTO );
  1032.             }
  1033.         }
  1034.         break;
  1035.  
  1036.     case EV_ENTITY_SOUND:
  1037.         DEBUGNAME("EV_ENTITY_SOUND");
  1038.         //somewhat of a hack - weapon is the caller entity's index, trickedentindex is the proper sound channel
  1039.         if ( cgs.gameSounds[ es->eventParm ] ) {
  1040.             trap_S_StartSound (NULL, es->weapon, 0, cgs.gameSounds[ es->eventParm ], -1, -1 );
  1041.         } else {
  1042.             s = CG_ConfigString( CS_SOUNDS + es->eventParm );
  1043.             trap_S_StartSound (NULL, es->weapon, 0, CG_CustomSound( es->weapon, s ), -1, -1 );
  1044.         }
  1045.         break;
  1046.  
  1047.     case EV_GLASS_SHATTER:
  1048.         DEBUGNAME("EV_GLASS_SHATTER");
  1049.         CG_GlassShatter(es->number, es->pos.trBase, es->angles, es->origin);
  1050.         break;
  1051.  
  1052.     case EV_PAIN:
  1053.         // local player sounds are triggered in CG_CheckLocalSounds,
  1054.         // so ignore events on the player
  1055.         DEBUGNAME("EV_PAIN");
  1056.         if ( cent->currentState.number != cg.snap->ps.clientNum ) 
  1057.         {
  1058.             CG_PainEvent( cent, es->eventParm );
  1059.         }
  1060.         break;
  1061.  
  1062.     case EV_PAIN_WATER:
  1063.     {
  1064.         static drownIndex = 0;
  1065.         DEBUGNAME("EV_PAIN_WATER");
  1066.         trap_S_StartSound ( NULL, es->number, CHAN_VOICE, cgs.media.drownPainSound[(drownIndex++)%2], -1, -1 );
  1067.         break;
  1068.     }
  1069.  
  1070.     case EV_GAME_OVER:
  1071.         DEBUGNAME("EV_OBITUARY");
  1072.         CG_GameOver ( es );
  1073.         break;
  1074.  
  1075.     case EV_GOGGLES:
  1076.         DEBUGNAME("EV_GOGGLES");
  1077.  
  1078.         // Sound is handled elsewhere for local client
  1079.         trap_S_StartSound ( NULL, es->number, CHAN_AUTO, es->eventParm?cgs.media.gogglesOnSound:cgs.media.gogglesOffSound, 120, -1 );
  1080.         break;
  1081.  
  1082.     case EV_OBITUARY:
  1083.         DEBUGNAME("EV_OBITUARY");
  1084.         CG_Obituary( es );
  1085.         break;
  1086.  
  1087.     case EV_STOPLOOPINGSOUND:
  1088.         DEBUGNAME("EV_STOPLOOPINGSOUND");
  1089.         trap_S_StopLoopingSound( es->number );
  1090.         es->loopSound = 0;
  1091.         break;
  1092.  
  1093.     case EV_DEBUG_LINE:
  1094.         DEBUGNAME("EV_DEBUG_LINE");
  1095.         CG_Beam( cent );
  1096.         break;
  1097.  
  1098.     case EV_TESTLINE:
  1099.         DEBUGNAME("EV_TESTLINE");
  1100.         CG_TestLine(es->origin, es->origin2, 0, es->weapon, 1);
  1101.         break;
  1102.  
  1103.     case EV_BODY_QUEUE_COPY:
  1104.         DEBUGNAME("EV_BODY_QUEUE_COPY");
  1105.  
  1106.         // First byte of eventParm is the client Number
  1107.         // Second byte of eventParm is the direction of the incoming shot
  1108.         // Third and fourth byte of eventParm is the hit location
  1109.         ByteToDir( (es->eventParm & 0xFF), dir );
  1110.  
  1111.         CG_BodyQueueCopy(cent, es->otherEntityNum, (es->eventParm>>8), dir);
  1112.         break;
  1113.  
  1114.     case EV_PROC_GORE:
  1115.         DEBUGNAME("EV_PROC_GORE");
  1116.         CG_AddProcGore(cent);
  1117.         break;
  1118.  
  1119.     case EV_BOTWAYPOINT:
  1120.         DEBUGNAME("EV_BOTWAYPOINT");
  1121.         CG_TestLine(cent->lerpOrigin, es->angles, 30000, 0x0000ff, 3);
  1122.         //Just render for 30 seconds because this waypoint might not be rendered again for quite some time.
  1123.         break;
  1124.  
  1125.     case EV_GAMETYPE_RESTART:
  1126.         CG_MapRestart ( qtrue );
  1127.         break;
  1128.  
  1129.     case EV_USE:
  1130.         break;
  1131.  
  1132.     case EV_WEAPON_CALLBACK:
  1133.         DEBUGNAME("EV_WEAPON_CALLBACK");
  1134.         if ( cent->currentState.number == cg.snap->ps.clientNum ) 
  1135.         {
  1136.             CG_WeaponCallback ( &cg.predictedPlayerState,
  1137.                                 &cg_entities[cg.predictedPlayerState.clientNum], 
  1138.                                 (es->eventParm&0xFF),            // Weapon id
  1139.                                 ((es->eventParm>>8)&0xFF),        // Anim id
  1140.                                 ((es->eventParm>>16)&0xFF),        // Anim choice
  1141.                                 ((es->eventParm>>24)&0xFF) );    // Callback step
  1142.         }
  1143.         break;
  1144.  
  1145.     default:
  1146.         DEBUGNAME("UNKNOWN");
  1147.         Com_Error( ERR_FATAL, "Unknown event: %i", event );
  1148.         break;
  1149.     }
  1150.  
  1151. }
  1152.  
  1153. /*
  1154. ==============
  1155. CG_CheckEvents
  1156. ==============
  1157. */
  1158. void CG_CheckEvents( centity_t *cent ) 
  1159. {
  1160.     // check for event-only entities
  1161.     if ( cent->currentState.eType > ET_EVENTS ) 
  1162.     {
  1163.         // already fired
  1164.         if ( cent->previousEvent ) 
  1165.         {            
  1166.             return;    
  1167.         }
  1168.         
  1169.         // if this is a player event set the entity number of the client entity number
  1170.         if ( cent->currentState.eFlags & EF_PLAYER_EVENT )
  1171.         {
  1172.             cent->currentState.number = cent->currentState.otherEntityNum;
  1173.         }
  1174.  
  1175.         cent->previousEvent = 1;
  1176.  
  1177.         cent->currentState.event = cent->currentState.eType - ET_EVENTS;
  1178.     } 
  1179.     else 
  1180.     {
  1181.         // check for events riding with another entity
  1182.         if ( cent->currentState.event == cent->previousEvent ) 
  1183.         {
  1184.             return;
  1185.         }
  1186.  
  1187.         cent->previousEvent = cent->currentState.event;
  1188.  
  1189.         if ( ( cent->currentState.event & ~EV_EVENT_BITS ) == 0 ) 
  1190.         {
  1191.             return;
  1192.         }
  1193.     }
  1194.  
  1195.     // calculate the position at exactly the frame time
  1196.     BG_EvaluateTrajectory( ¢->currentState.pos, cg.snap->serverTime, cent->lerpOrigin );
  1197.  
  1198.     CG_SetEntitySoundPosition( cent );
  1199.  
  1200.     CG_EntityEvent( cent, cent->lerpOrigin );
  1201. }
  1202.  
  1203.